#pragma once

#include <iostream>
#include <string>
#include <functional>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <pthread.h>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include "err.hpp"
#include "ThreadPool.hpp"
#include "Task.hpp"
#include "log.hpp"

namespace ns_server
{
    static const uint16_t DEFAULTPORT = 8888;
    static const int backlog = 32; // TODO

    using func_t = std::function<std::string(const std::string &)>;

    class TCPServer;


    class ThreadData
    {
    public:
        ThreadData(int sock, const std::string& ip, const uint16_t port, TCPServer* ts)
            : _sock(sock), _clientip(ip), _clientport(port),_current(ts)
        { }
    public:
        int _sock;
        std::string _clientip;
        uint16_t _clientport;
        TCPServer *_current;
    };

    class TCPServer
    {
    public:
        TCPServer(func_t func, uint16_t port = DEFAULTPORT)
            : _port(port), _quit(true) , _func(func)
        {
        }

        ~TCPServer()
        {
        }

        void initServer()
        {
            // 1.创建套接字文件
            // SOCK_STREAM     Provides sequenced, reliable, two-way,
            // connection-based byte streams.  An  out-
            // of-band data transmission mechanism may be supported.
            _listenSock = socket(AF_INET, SOCK_STREAM, 0);
            if (_listenSock < 0)
            {
                // std::cerr << "Create socket error.\n";
                logMessage(FATAL, "Create socket error\n");
                exit(SOCKET_ERR);
            }

            // 2.绑定
            struct sockaddr_in local;
            memset(&local, 0, sizeof(local));
            local.sin_family = AF_INET;
            local.sin_port = htons(_port);
            local.sin_addr.s_addr = INADDR_ANY;
            if (bind(_listenSock, (struct sockaddr *)&local, sizeof(local)) < 0)
            {
                // std::cerr << "Bind error\n";
                logMessage(FATAL, "Bind socket error\n");
                exit(BIND_ERR);
            }

            // 3.监听 listen
            if (listen(_listenSock, backlog) < 0)
            {
                logMessage(FATAL, "Listen error\n");
                // std::cerr << "Listen error\n";
                exit(LISTEN_ERR);
            }
        }
        void start()
        {
            // signal(SIGCHLD, SIG_IGN);

            _quit = false;
            while (!_quit)
            {
                // 4.获取连接 accept
                // socket()返回的是socket文件描述符
                // accept()返回的也是socket文件描述符
                // 前者是监听套接字，是揽客的，只有一个。
                // 后者是真正提供IO服务的，是服务员，可以有多个。
                struct sockaddr_in client;
                socklen_t len = sizeof(client);
                int sock = accept(_listenSock, (struct sockaddr *)&client, &len);
                if (sock < 0)
                {
                    // std::cerr << "Accept error.\n";
                    logMessage(WARNING, "Accept error\n");
                    continue; // 这次揽客失败，继续回去揽客
                }
                std::string clientip = inet_ntoa(client.sin_addr);
                uint16_t clientport = ntohs(client.sin_port);

                // std::cout << "获取新连接成功, listenSock: " << _listenSock << ", sock: " << sock <<" | ";
                // std::cout << "Clientip-Clientport: [" << clientip << "-" << clientport << "]\n";

                logMessage(INFO, "获取连接成功, listenSock:%d, sock:%d, Clientip-Clientport:[%s-%d]", _listenSock, sock, clientip.c_str(), clientport);

                // // v1: 单进程
                // service(sock, clientip, clientport);

                // v2: 多进程
                // pid_t id = fork();
                // if(id < 0)
                // {
                //     close(sock);
                //     continue;
                // }
                // else if(id == 0) //child。 子进程会拷贝父进程的fd表
                // {
                //     // 子进程建议关闭不用的fd
                //     close(_listenSock);
                //     service(sock, clientip, clientport);

                //     exit(0);
                // }

                // // 父进程一定要关掉不用的fd，因为这个fd给子进程去处理了，父进程就用不到了，而且一旦很多个客户端连接，这些文件描述符会越来越少
                // // “文件描述符泄漏”
                // close(sock);

                // // pid_t ret = waitpid(id, nullptr, 0); //父进程是阻塞性等待，不好。直接忽略子进程退出信号。
                // // if(ret == id)
                // //     std::cout << "Wait child " << id << "success\n";

                // v3: 多线程 -- 原生线程库
                // 1.其他线程不能关闭用不到的fd，因为所有线程共享同一张fd表，主线程还需要用到_listenSocket
                // 2.其他线程分离，不要让主线程阻塞等待。
                // pthread_t tid;
                // ThreadData *td = new ThreadData(sock, clientip, clientport, this);
                // pthread_create(&tid, nullptr, threadRoutine, td);

                // v4: 多线程 -- 线程池版的
                // 使用线程池时，线程个数有限，因此一定要处理短任务，而不是像TCPServer::service一样的死循环
                Task t(sock, clientip, clientport, std::bind(&TCPServer::service, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
                ThreadPool<Task>::getinstance()->pushTask(t);
            }
        }

        static void* threadRoutine(void* args)
        {
            pthread_detach(pthread_self());
            ThreadData *td = static_cast<ThreadData *>(args);
            td->_current->service(td->_sock, td->_clientip, td->_clientport);

            delete td;
        }

        void service(int sock, const std::string &clientip, const uint16_t clientport)
        {
            std::string who = "[" + clientip + "-" +std::to_string(clientport) + "]";
            char buf[1024];
            while (true)
            {
                ssize_t n = read(sock, buf, sizeof(buf));
                if (n > 0)
                {
                    buf[n] = '\0';
                    // std::cout << who << "# " << buf << std::endl;
                    std::string res = _func(buf); //回调

                    logMessage(DEBUG, "%s#%s\n", who.c_str(), buf);

                    write(sock, res.c_str(), res.size());
                }
                else if (n == 0)
                {
                    // 读到文件结尾，意味着客户端关闭连接了,我们也关闭
                    close(sock);
                    // std::cout << who << " client Quit.\n";
                    logMessage(INFO, "%s client quit.\n", who.c_str());
                    break;
                }
                else
                {
                    close(sock);
                    // std::cerr << "Read error" << strerror(errno) << std::endl;
                    logMessage(ERROR, "Read error %d:%s", errno, strerror(errno));
                    break;
                }
            }
        }

    private:
        uint16_t _port;
        int _listenSock;
        bool _quit;
        func_t _func;
    };
}